#region License
// PropertiesMustMatchAttribute.cs
//
// The base implementation of this file is derived from Visual Studio 2012 RC templates.
#endregion

namespace Kaqaz.Common
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Linq;
    using System.Text;

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class PropertiesMustMatchAttribute : ValidationAttribute
    {
        private static readonly string DefaultErrorMessage = Resources.Resource.PropertiesDontMatch;
        private readonly object typeId = new object();
        private string errorMessageResourceName;

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertiesMustMatchAttribute" /> class.
        /// </summary>
        /// <param name="originalProperty">The original property.</param>
        /// <param name="confirmProperty">The confirm property.</param>
        public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
            : this(originalProperty, originalProperty, confirmProperty, confirmProperty)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertiesMustMatchAttribute" /> class.
        /// </summary>
        /// <param name="originalProperty">The original property.</param>
        /// <param name="originalPropertyLabelResourceName">Name of the original property label resource.</param>
        /// <param name="confirmProperty">The confirm property.</param>
        /// <param name="confirmPropertyLabelResourceName">Name of the confirm property label resource.</param>
        public PropertiesMustMatchAttribute(string originalProperty, string originalPropertyLabelResourceName, string confirmProperty, string confirmPropertyLabelResourceName)
            : base(DefaultErrorMessage)
        {
            this.OriginalProperty = originalProperty;
            this.OriginalPropertyLabelResourceName = originalPropertyLabelResourceName;
            this.ConfirmProperty = confirmProperty;
            this.ConfirmPropertyLabelResourceName = confirmPropertyLabelResourceName;
        }

        /// <summary>
        /// Gets the confirm property.
        /// </summary>
        /// <value>
        /// The confirm property.
        /// </value>
        public string ConfirmProperty { get; private set; }

        /// <summary>
        /// Gets the name of the original property label resource.
        /// </summary>
        /// <value>
        /// The name of the original property label resource.
        /// </value>
        public string OriginalPropertyLabelResourceName { get; private set; }

        /// <summary>
        /// Gets the original property.
        /// </summary>
        /// <value>
        /// The original property.
        /// </value>
        public string OriginalProperty { get; private set; }

        /// <summary>
        /// Gets the name of the confirm property label resource.
        /// </summary>
        /// <value>
        /// The name of the confirm property label resource.
        /// </value>
        public string ConfirmPropertyLabelResourceName { get; private set; }

        /// <summary>
        /// Gets the type id.
        /// </summary>
        /// <value>
        /// The type id.
        /// </value>
        public override object TypeId
        {
            get
            {
                return this.typeId;
            }
        }

        /// <summary>
        /// Gets or sets the name of the error message resource.
        /// </summary>
        /// <value>
        /// The name of the error message resource.
        /// </value>
        public new string ErrorMessageResourceName
        {
            get 
            { 
                return this.errorMessageResourceName; 
            }

            set
            {
                this.errorMessageResourceName = value;
                this.ErrorMessage = Resources.Resource.ResourceManager.GetString(this.errorMessageResourceName);
            }
        }

        /// <summary>
        /// Applies formatting to an error message, based on the data field where the error occurred.
        /// </summary>
        /// <param name="name">The name to include in the formatted message.</param>
        /// <returns>
        /// An instance of the formatted error message.
        /// </returns>
        public override string FormatErrorMessage(string name)
        {
            return string.Format(
                CultureInfo.CurrentUICulture, 
                this.ErrorMessageString,
                Resources.Resource.ResourceManager.GetString(this.OriginalPropertyLabelResourceName),
                Resources.Resource.ResourceManager.GetString(this.ConfirmPropertyLabelResourceName));
        }

        /// <summary>
        /// Determines whether the specified value of the object is valid.
        /// </summary>
        /// <param name="value">The value of the object to validate.</param>
        /// <returns>
        /// true if the specified value is valid; otherwise, false.
        /// </returns>
        public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            object originalValue = properties.Find(this.OriginalProperty, true /* ignoreCase */).GetValue(value);
            object confirmValue = properties.Find(this.ConfirmProperty, true /* ignoreCase */).GetValue(value);
            return object.Equals(originalValue, confirmValue);
        }
    }
}
